package com.zh.chat.net;

/*
 TCP通讯
 需求:传送任意文件

 tcp传输要定义2个端点,客户端和服务端
 步骤
 1.定义服务, 服务器ip和接收端口
 2.读取客户端已有的图片文件
 3.使用socket输出流将数据发给服务端
 4.读取服务端的反馈信息
 5.关闭资源

 注意:
 1.在while循环中的read方法最后读到文件结束标记-1后循环退出了
 而没有将该标记写入socket流中,那么服务端接收到的数据是不完整的,而且停不下来
 用shutdownOutput方法告诉服务端文件到了末尾结束发送了

 [示例]:传送任意文件 (客户端)
 */

import java.net.*;
import java.io.*;

public class FileClient // 客户端
{
	private Socket sock = null;

	/**
	 * @param path
	 *            接收文件的目录
	 * @param ip
	 * @param port
	 * @throws Exception
	 */
	public FileClient(String path, String ip, int port) throws Exception {
		File file = new File(path); // 关联一个文件
		if (!file.exists()) {
			file.mkdirs();
		}
		sock = new Socket(ip, port);
		try {
			InputStream sockIn = sock.getInputStream();// 定义socket输入流,接收客户端的信息
			writeOutInfo(sock, "FileSendNow"); // 告诉客户端,开始发送文件名数据吧
			// 获取客户端请求发送的文件名,并判断在D盘创建同名文件的情况
			byte[] bufName = new byte[1024];
			int lenInfo = 0;
			lenInfo = sockIn.read(bufName); // 获取文件名
			String retStr = new String(bufName, 0, lenInfo);
			String[] infos = retStr.split(":");
			String fileName = infos[0];
			Long totalLength = Long.parseLong(infos[1]);
			file = getClientFileName(sockIn, path, fileName); // 创建同名文件
			if (file == null) {
				writeOutInfo(sock, "文件失败,服务端断开连接!");
				sock.close();
				return;
			}

			FileOutputStream fos = new FileOutputStream(file, false); // 用覆盖模式来写入硬盘
			byte[] bufFile = new byte[1024 * 1024]; // 接收数据的缓存
			int len = 0;
			lenInfo = 0;
			writeOutInfo(sock, "ok");
			while (true) {
				len = sockIn.read(bufFile); // 接收数据
				if (len != -1) {
					fos.write(bufFile, 0, len); // 写入硬盘文件
					lenInfo+=len;
					if(lenInfo==totalLength){
						break;
					}
				}
			}
			writeOutInfo(sock, "over");
			System.out.println("文件接收成功!"); // 客户端打印一下
			fos.flush();
			fos.close();
		} catch (Exception ex) {
			throw new RuntimeException(ip + "异常!!!"+path+"文件没有接收成功");
		}
	}

	private File getClientFileName(InputStream sockIn, String path, String fileName)
			throws Exception // 获取文件名并创建
	{
		File dir = new File(path); // 存到path目录
		File[] files = dir.listFiles(); // 遍历path目录
		for (File f : files) {
			if (!f.isDirectory()) // 如果遍历到的该文件不是一个目录的话
			{
				if (f.getName().equals(fileName)) // 判断是否是同名文件
				{
					f.delete();
				}
			}
		}
		File file = new File(path + fileName);
		if (file.createNewFile()) {
			System.out.println("成功创建文件(" + file.getAbsolutePath() + " )准备写入数据");
			return file;
		} else {
			return null; // 如果由于硬盘满了等原因创建文件失败的话
		}
	}

	private String servInfoBack(Socket sock) throws Exception // 读取服务端的反馈信息
	{
		InputStream sockIn = sock.getInputStream(); // 定义socket输入流
		byte[] bufIn = new byte[1024];
		int lenIn = sockIn.read(bufIn); // 将服务端返回的信息写入bufIn字节缓冲区
		String info = new String(bufIn, 0, lenIn);
		return info;
	}

	private void writeOutInfo(Socket sock, String infoStr) throws Exception// 将信息反馈给服务端
	{
		OutputStream sockOut = sock.getOutputStream();
		sockOut.write(infoStr.getBytes());
	}
}